package org.raymond.iworks.study.netty.official.fundamental.time;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * @author raymond
 * @version V1.0
 * @Description: TODO
 */

/**
 *    因为我们将忽略任何接收到的数据,而是在连接建立后立即发送消息,所以这次我们不能使用channelRead()方法.相反,
 *  我们应该重写channelActive()方法.
 *
 *     ByteBuf没有ByteBuffer的flip那样的方法,因为它有两个指针:一个用于读操作,另一个用于写操作.
 * 当向ByteBuf写入内容时,写入器索引增加,而读取器索引不变,reader索引和writer索引分别表示消息的开始和结束位置.
 * writer index和reader index互不影响.
 *     NIO缓冲区没有提供一种不需要flip的干净的方法来确定消息内容的开始和结束位置.当您忘记翻转缓冲区时,您将遇到麻烦,
 * 因为不会发送任何数据或发送了不正确的数据.这样的错误在Netty中不会发生,因为我们对不同的操作类型有不同的指针.
 *     另一点需要注意的是,ChannelHandlerContext.write()(和writeAndFlush())方法返回一个ChannelFuture.
 * ChannelFuture表示一个尚未发生的I/O操作.这意味着,任何请求的操作都可能尚未执行,因为所有操作在Netty中都是异步的.
 *     例如,下面的代码可能会在发送消息之前关闭连接:
 * Channel ch = ...;
 * ch.writeAndFlush(message); // 这是异步的
 * ch.close(); // 应该放在监听器中
 *     因此,您需要在ChannelFuture完成后调用close()方法(该方法由write()方法返回),当write操作完成时会通知相应的侦听器.
 * 请注意,close()也可能不会立即关闭连接,它返回一个ChannelFuture.
 */
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
    // 当客户端和服务器的连接已建立将会调用channelActive()方法.在这方法中我们向客户端发送一个当前时间的32位整数
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // Get the current ByteBufAllocator via ChannelHandlerContext.alloc() and allocate a new buffer.
        final ByteBuf time = ctx.alloc().buffer(4); // 发送一个32bit整数,需要一个容量为4字节的bytebuffer
        time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));

        // for telnet show
        //String now = LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME);
        //final ByteBuf time = ctx.alloc().buffer(20);
        //time.writeCharSequence(now, StandardCharsets.UTF_8);

        final ChannelFuture f = ctx.writeAndFlush(time);
        f.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) {
                assert f == future;
                ctx.close();
            }
        });
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
